//============================================================================================
/**
 * @file	steal_ai.c
 * @brief	フラッグAI
 * @author	Nozomu Saito
 *
 * モジュール名：StealAI_
 */
//============================================================================================

#include "steal_ai.h"
#include "steal_comm.h"

#define FRONT_CHK_LEN	(160)
#define BACK_CHK_LEN	(80)

#define CELL_W_NUM	(6)
#define CELL_H_NUM	(8)
#define CELL_MAX	(CELL_W_NUM*CELL_H_NUM)
#define CELL_WIDTH	(32)		//ＡＩセル幅ドット単位
#define CELL_X_OFS	( -(CELL_WIDTH/2*5) )
#define CELL_Y_OFS	( -(CELL_WIDTH/2*3) )

#define LINE_NUM	(4)

#define POKE_NUM	(ENTRY_MEMBER_NUM_MAX)

#define EXE_FRAME_ALONE	(6)			//非通信時AI実行フレーム間隔
#define EXE_FRAME_COMM	(6)			//通信時AI実行フレーム間隔

#define NPC_CHG_AT_VAL	(20)		//ＮＰＣが交代をするスタミナ量

#ifdef PM_DEBUG

#endif

typedef struct STEAL_CELL_INFO_tag
{
	int CX;
	int CY;
	int X;
	int Y;
	s16 Grading;
	u16 Index;
}CELL_INFO;

typedef  struct STEAL_INFO_NODE_tag INFO_NODE;

typedef struct STEAL_INFO_NODE_tag
{
	INFO_NODE *Prev;
	INFO_NODE *Next;
	CELL_INFO *Info;
}INFO_NODE;

typedef struct STEAL_POKE_INFO_tag
{
	STL_POKE *StealPoke;
	u16 X;
	u16 Y;
}AI_POKE_INFO;

typedef struct STEAL_OBJ_INFO_tag
{
	BOOL Valid;
	int X;
	int Y;
}AI_OBJ_INFO;
/**
typedef struct POKE_POS_tag
{
	int X;
	int Y;
	int Line;
}POKE_POS;
*/
typedef struct STEAL_AI_CONT_tag
{
	AI_POKE_INFO PokeInfo[POKE_NUM];		//ポケモン情報
	AI_OBJ_INFO ObstInfo[OBST_NUM_MAX];	//障害物情報
	AI_OBJ_INFO ParaInfo[PARASOL_NUM_MAX];		//パラソル情報
	STL_FLAG	*FlagPtr;		//フラッグ
	u8 PokeLine[POKE_NUM];
	CELL_INFO Cell[CELL_MAX];
	BOOL Execute;
	u8 NpcNum;	//0〜3
	u8 ExeNpcIdx;	//0〜2
	u8 Frame;
	u8 CntFrame;
	u8 NpcStartIdx;	//1〜3
	u8 PlayerNum;	//1〜4
	u8 dummy[2];
}STEAL_AI_CONT;

static CELL_INFO *GetCell(STEAL_AI_CONT_PTR ptr);
static u8 SortInfo(CELL_INFO **inInfo, CELL_INFO **outInfo);
static void ExecuteAI(STEAL_AI_CONT_PTR ptr, const u8  inTarget);
static BOOL CheckCellIO(const CELL_INFO *inCell, const u16 inX, const u16 inY );
static BOOL CheckFieldIO(const CELL_INFO *inCell);

//----------------------------------------------------------------------------
/**
 *	@brief	初期化
 *
 *	@param	inHeapID		ヒープID
 *	@param	inNPCNum		NPCの数
 *	@param	inType			ゲームタイプ（通信してるか）
 *
 *	@return ptr				コントロールポインタ
 */
//-----------------------------------------------------------------------------
STEAL_AI_CONT_PTR StealAI_Init(const int inHeapID, const int inNpcNum, const PKTHLN_GAME_TYPE inType)
{
	STEAL_AI_CONT_PTR cnt_ptr;
	//メモリアロケーション
	int size = sizeof(STEAL_AI_CONT);
	cnt_ptr = sys_AllocMemory(inHeapID, size);
	MI_CpuClear8( cnt_ptr, size);

	cnt_ptr->NpcNum = inNpcNum;
	cnt_ptr->PlayerNum = ENTRY_MEMBER_NUM_MAX-inNpcNum;
	cnt_ptr->NpcStartIdx = cnt_ptr->PlayerNum;
	if (inType == PKTHLN_GAME_ALONE){
		cnt_ptr->CntFrame = EXE_FRAME_ALONE;
	}else{
		cnt_ptr->CntFrame = EXE_FRAME_COMM;
	}
	cnt_ptr->Frame = cnt_ptr->CntFrame;	//すぐに起動するようにしておく
	return cnt_ptr;
}


//----------------------------------------------------------------------------
/**
 *	@brief	ポケモンデータのセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inIdx			プレーヤーインデックス
 *	@param	*inStealPoke		ポケモンデータへのポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void StealAI_SetupPokeInfo(	STEAL_AI_CONT_PTR ptr,
							const u8 inIdx,
							STL_POKE *ioStealPoke )
{
	ptr->PokeInfo[inIdx].StealPoke = ioStealPoke;
}

//----------------------------------------------------------------------------
/**
 *	@brief	フラッグデータのセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	*inStealFlag	フラッグデータへのポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void StealAI_SetupFlagPtr(	STEAL_AI_CONT_PTR ptr, STL_FLAG *inStealFlag )
{
	ptr->FlagPtr = inStealFlag;
}


//----------------------------------------------------------------------------
/**
 *	@brief	障害物データのセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inObstData			障害物データ先頭アドレス
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void StealAI_SetUpObstInfo(STEAL_AI_CONT_PTR ptr, const OBJ_CHAR_POS *inObstData)
{
	int i;
	u16 y;
	u16 x;
	for(i=0;i<OBST_NUM_MAX;i++){
		x = inObstData[i].X * 8;
		y = inObstData[i].Y * 8;

		if ( (x!=0)&&(y!=0) ){
			ptr->ObstInfo[i].Valid = TRUE;
			ptr->ObstInfo[i].X = x;
			ptr->ObstInfo[i].Y = y;
///			OS_Printf("OBST_POS=%d,%d\n",ptr->ObstInfo[i].X,ptr->ObstInfo[i].Y);
		}else{
			ptr->ObstInfo[i].Valid = FALSE;
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	パラソルデータのセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inParaData		パラソルデータ先頭アドレス
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void StealAI_SetupParaInfo(STEAL_AI_CONT_PTR ptr, const OBJ_CHAR_POS *inParaData)
{
	int i;
	u16 y;
	u16 x;
	for(i=0;i<PARASOL_NUM_MAX;i++){
		x = inParaData[i].X * 8;
		y = inParaData[i].Y * 8;

		if ( (x!=0)&&(y!=0) ){
			ptr->ParaInfo[i].Valid = TRUE;
			ptr->ParaInfo[i].X = x;
			ptr->ParaInfo[i].Y = y;
///			OS_Printf("PARA_POS=%d,%d\n",ptr->ParaInfo[i].X,ptr->ParaInfo[i].Y);
		}else{
			ptr->ParaInfo[i].Valid = FALSE;
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	解放
 *
 *	@param	ptr				コントロールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void StealAI_End(STEAL_AI_CONT_PTR ptr)
{
	//メモリ解放
	sys_FreeMemoryEz(ptr);
}


//----------------------------------------------------------------------------
/**
 *	@brief	ＡＩ実行

 *	@param	ptr			管理ポインタ
 *
 *	@return u8		動かしたＮＰＣインデックス
 */
//-----------------------------------------------------------------------------
u8 StealAI_Execute(STEAL_AI_CONT_PTR ptr)
{
	u8 exe_idx;
	exe_idx = POKE_NUM;

	if (ptr->PlayerNum == ENTRY_MEMBER_NUM_MAX){
		//AIなし
		return 0;
	}

	ptr->Frame++;

	if (ptr->Frame >= ptr->CntFrame){
		//実行状態にする
		ptr->Execute = TRUE;
		ptr->Frame = 0;
	}

	if (ptr->Execute){	//実行状態
		int exe_poke;
		//実行NPCのポケ決定
		exe_poke = ptr->NpcStartIdx + ptr->ExeNpcIdx;
		GF_ASSERT(exe_poke >= ptr->NpcStartIdx );
		//実行
		ExecuteAI(ptr, exe_poke);
		exe_idx = exe_poke;
		//実行NPCのシフト
		ptr->ExeNpcIdx++;
		if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
			ptr->ExeNpcIdx = 0;
			//実行フラグを落とす
			ptr->Execute = FALSE;
		}
	}
	return exe_idx;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行
 *
 *	@param	ptr				コントローラポインタ
 *	@param	inTarget		対象ポケインデックス
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void ExecuteAI(STEAL_AI_CONT_PTR ptr, const u8  inTarget)
{
	int chg_grading;
	CELL_INFO *cell;
	int i, j;
	AI_POKE_INFO *target_info;
	AI_POKE_INFO poke_info[POKE_NUM];
	AI_OBJ_INFO obst_info[OBST_NUM_MAX];
	AI_OBJ_INFO para_info[PARASOL_NUM_MAX];
	STL_FLAG	*flag_ptr;
	STL_POKE	*target_poke;

	int line_point[LINE_NUM];

	//デバッグ
///	if(inTarget != 1) return ;

	target_info = &ptr->PokeInfo[inTarget];
	target_poke = target_info->StealPoke;
	//ポーズ中、気絶中、ヒットポーズ中は処理をスルー
	{
		if ( target_poke->Pause ||
			 (target_poke->State == POKE_ST_PANIC) ||
			 target_poke->HitFrame ){
///			OS_Printf("ＡＩ行動不可\n");
			return;
		}
	}

	//すべてのポケ(4体分)の座標をセット。
	for(i=0;i<POKE_NUM;i++){
		STL_POKE *poke;
		int tsx,tsy;
		fx32 x,y;

		poke_info[i] = ptr->PokeInfo[i];
		poke = poke_info[i].StealPoke;
		PokeAct_GetTouchPosByShadowPos(poke->PokeActPtr[poke->StlPokeIdx],
					poke->Pos.x/FX32_ONE, poke->Pos.y/FX32_ONE, &tsx, &tsy);
		PokeAct_GetHitPosByTouchPos(poke->PokeActPtr[poke->StlPokeIdx],
				tsx*FX32_ONE, tsy*FX32_ONE, &x, &y);

		poke_info[i].X = x / FX32_ONE;
		poke_info[i].Y = y / FX32_ONE;
///		OS_Printf("pokeinfoXY = %d,%d\n",poke_info[i].X,poke_info[i].Y);
	}
	//セットしなおし
	target_info = &poke_info[inTarget];
	//すべての障害物の座標をセット
	for(i=0;i<OBST_NUM_MAX;i++){
		obst_info[i] = ptr->ObstInfo[i];
	}
	//すべてのパラソルの座標をセット
	for(i=0;i<PARASOL_NUM_MAX;i++){
		para_info[i] = ptr->ParaInfo[i];
	}
	//旗ポインタセット
	flag_ptr = ptr->FlagPtr;

	//自分の現在座標から、セル中心座標を算出
	for(i=0;i<CELL_MAX;i++){
		ptr->Cell[i].Index = i;
		ptr->Cell[i].CX = target_info->X + CELL_X_OFS + (i%CELL_W_NUM)*CELL_WIDTH;
		ptr->Cell[i].CY = target_info->Y + CELL_Y_OFS + (i/CELL_W_NUM)*CELL_WIDTH;
		//選出先決定
		ptr->Cell[i].X = target_info->X - (CELL_WIDTH*(CELL_W_NUM/2)) + (CELL_WIDTH*(i%CELL_W_NUM)) + (gf_rand()%CELL_WIDTH);
		ptr->Cell[i].Y = target_info->Y - (CELL_WIDTH*(CELL_H_NUM/2)) + (CELL_WIDTH*(i/CELL_W_NUM)) + (gf_rand()%CELL_WIDTH);
///		OS_Printf("CELLXY = %d,%d\n",ptr->Cell[i].X,ptr->Cell[i].Y);
	}
	//座標算出が終了したら、評価を始める。
	{
		//セルの評点を0クリア
		for(i=0;i<CELL_MAX;i++){
			ptr->Cell[i].Grading = 0;
		}
		//自分の周りは+2
		for(i=0;i<2;i++){
			for(j=0;j<4;j++){
				u8 val = (CELL_H_NUM/2-1)*CELL_W_NUM;		//((i+1)%4)*CELL_W_NUM;
				u8 idx = val + (j+1);
				ptr->Cell[idx].Grading += 2;
			}
		}

		for(i=0;i<CELL_MAX;i++){
			cell = &ptr->Cell[i];
			//旗のあるセルは+8
			for(j=0;j<FLAG_DISP_MAX;j++){
				if (!flag_ptr[j].Valid){
					continue;
				}
///				OS_Printf("FLAG%d = %d,%d\n",j,flag_ptr[j].X, flag_ptr[j].Y);
				if ( CheckCellIO(cell, flag_ptr[j].X, flag_ptr[j].Y) ){
					cell->Grading += 7;
				}
			}
			//ポケモンのいるセルは-3
			for(j=0;j<POKE_NUM;j++){
				//自分は対象外
				if (j == inTarget){
					continue;
				}
				if ( CheckCellIO(cell, poke_info[j].X, poke_info[j].Y) ){
					cell->Grading -= 3;
				}
			}
			//障害物のあるセルは-2
			for(j=0;j<OBST_NUM_MAX;j++){
				if (!obst_info[j].Valid){
					break;
				}
				if ( CheckCellIO(cell, obst_info[j].X, obst_info[j].Y) ){
					cell->Grading -= 2;
				}
			}
			//パラソルのあるセルは+2
			for(j=0;j<PARASOL_NUM_MAX;j++){
				if (!para_info[j].Valid){
					break;
				}
				if ( CheckCellIO(cell, para_info[j].X, para_info[j].Y) ){
					cell->Grading += 2;
				}
			}
			//セルの目的座標が、移動範囲外の場合は-100
			if ( !CheckFieldIO(cell) ){
				cell->Grading -= 100;
			}
		}//end for(i=0;i<CELL_MAX;i++)

		//Ｙ座標で評点変化
		{
			u16 target_y;
			target_y = target_poke->Pos.y/FX32_ONE;
			if (target_y > 256){
				u8 start;
				start = (CELL_H_NUM/2+1)*CELL_W_NUM;
				for (i=0;i<CELL_W_NUM;i++){
					ptr->Cell[start+i].Grading += 3;
				}
			}else if(target_y < 256){
				for (i=0;i<CELL_W_NUM;i++){
					ptr->Cell[i].Grading += 3;
				}
			}
		}

		//旗を持っているとき
		if (target_poke->FlagNum){
			u16 target_x;
			u8 start;
			start = (CELL_H_NUM/2+1)*CELL_W_NUM;
			//下のセル群に評点加算
			for (i=0;i<CELL_W_NUM;i++){
				ptr->Cell[start+i].Grading += (target_poke->FlagNum);
			}
			target_x = target_poke->Pos.x/FX32_ONE;
			if(target_x > 256){			//ポケのＸ位置が256より大きい
				for (i=0;i<CELL_W_NUM/2;i++){
					ptr->Cell[start+i].Grading += 2;
				}
			}else if(target_x < 256){	//ポケのＸ位置が256より小さい
				for (i=0;i<CELL_W_NUM/2;i++){
					ptr->Cell[start+(CELL_W_NUM/2)+i].Grading += 2;
				}
			}
		}
	}
/**
	//デバッグ
	for(i=0;i<CELL_MAX;i++){
		OS_Printf("%d_GRADING = %d\n",i,ptr->Cell[i].Grading);
	}
*/
	//一番評点の高いセルを取得(最高点が同じ場合はランダム)
	cell = GetCell(ptr);
///	OS_Printf("cellidx = %d\n",cell->Index);
///	OS_Printf("XY = %d,%d\n",cell->X, cell->Y);

	//プレーヤーの最終目的地等は決して書き換えられないようにする
	if ( inTarget >= ptr->NpcStartIdx ){
		//開始位置セット
		{
			u8 steal_idx;
			int x,y;
			steal_idx = target_info->StealPoke->StlPokeIdx;
			PokeAct_GetTouchPosByShadowPos(target_info->StealPoke->PokeActPtr[steal_idx],
									target_info->X, target_info->Y, &x, &y);
			target_info->StealPoke->TPos.x = x * FX32_ONE;
			target_info->StealPoke->TPos.y = y * FX32_ONE;
			target_info->StealPoke->TPos.z = 0;
///			OS_Printf("現在位置：%d,%d__目的地%d,%d\n",x,y,cell->X,cell->Y);
		}
		//目的座標セット(セル内に旗があるときは、目的座標を旗の位置に再設定)
		target_info->StealPoke->LastTPos.x = cell->X * FX32_ONE;
		target_info->StealPoke->LastTPos.y = cell->Y * FX32_ONE;
		target_info->StealPoke->LastTPos.z = 0;
		//移動ベクトル処理実行
		target_info->StealPoke->MoveVecDcd = TRUE;
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	一番評価の高いセルを取得
 *
 *	@param	ptr					コントローラポインタ
 *
 *	@return	CELL_INFO *			セルポインタ
 */
//-----------------------------------------------------------------------------
static CELL_INFO *GetCell(STEAL_AI_CONT_PTR ptr)
{
	int i;
	u8 num;
	u8 idx;
	CELL_INFO *sort_work[CELL_MAX];
	CELL_INFO *src_cell[CELL_MAX];

	for(i=0;i<CELL_MAX;i++){
		src_cell[i] = &ptr->Cell[i];
	}
	num = SortInfo(src_cell, sort_work);
	//一番高い評価点のセルの中からランダムでひとつ選ぶ
	idx = gf_rand()%num;

	return sort_work[idx];
}

//----------------------------------------------------------------------------
/**
 *	@brief	データソート
 *
 *	@param	*inInfo		ソート前データ
 *	@param	*outInfo	ソート後データ
 *
 *	@return	count		一番大きい値を持った要素数の個数
 */
//-----------------------------------------------------------------------------
static u8 SortInfo(CELL_INFO **inInfo, CELL_INFO **outInfo)
{
	u8 count;
	u8 i,j;
	INFO_NODE node[1+CELL_MAX];

	//準備
	node[0].Prev = &node[1];
	node[0].Next = &node[1];
	node[0].Info = NULL;

	count = 0;

	for(i=0;i<CELL_MAX;i++){
		INFO_NODE *now;
		INFO_NODE *target;
		//ノード取得
		now = &node[i+1];
		//値セット
		now->Info = inInfo[i];
		now->Next = NULL;
		now->Prev = NULL;

		target = &node[0];
		for(j=0;j<i;j++){
			target = target->Next;
			//挿入
			if (target->Info->Grading < now->Info->Grading){	//値が大きい
				//リストの前に追加
				target->Prev->Next = now;
				now->Prev = target->Prev;
				now->Next = target;
				target->Prev = now;
				break;
			}
		}
		if (j==i){
			//リスト最後に追加
			target->Next = now;
			now->Prev = target;
		}
	}
	//ソート終了したら、バッファにデータを格納
	{
		INFO_NODE *now;
		u8 check;
		int max_val;
		//最大値の取得
		max_val = node[0].Next->Info->Grading;
		now = &node[0];
		for(i=0;i<CELL_MAX;i++){
			now = now->Next;
			outInfo[i] = now->Info;
			//一番高い値がいくつあるかカウント
			if (max_val == outInfo[i]->Grading){
				count++;
			}
		}
	}

	GF_ASSERT(count > 0);

	return count;
}

//----------------------------------------------------------------------------
/**
 *	@brief	セル内外判定
 *
 *	@param	inCell		セル
 *	@param	inX			Ｘ座標
 *	@param	inY			Ｙ座標
 *
 *	@return	BOOL		TRUE:セル内	FALSE:セル外
 */
//-----------------------------------------------------------------------------
static BOOL CheckCellIO(const CELL_INFO *inCell, const u16 inX, const u16 inY )
{
	int sx,sy,ex,ey;
	sx = inCell->X - (CELL_WIDTH/2);
	ex = inCell->X + (CELL_WIDTH/2);
	sy = inCell->Y - (CELL_WIDTH/2);
	ey = inCell->Y + (CELL_WIDTH/2);

	if ( (sx<=inX)&&(inX<ex)&&(sy<=inY)&&(inY<ey) ){
		return TRUE;
	}
	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	フィールド内外判定
 *
 *	@param	inCell		セル
 *
 *	@return	BOOL		TRUE:内	FALSE:外
 */
//-----------------------------------------------------------------------------
static BOOL CheckFieldIO(const CELL_INFO *inCell)
{
	if (inCell->Y < WALL_Y_LIMIT_MIN){
		return FALSE;
	}

	if (inCell->Y <= WALL_Y_LIMIT_MAX){
		if ( (WALL_X_LIMIT_MIN <= inCell->X)&(inCell->X <= WALL_X_LIMIT_MAX) ){
			return TRUE;
		}
	}else{
		if ( (GOAL_X_LIMIT_MIN <= inCell->X)&(inCell->X <= GOAL_X_LIMIT_MAX) ){
			return TRUE;
		}
	}
	return FALSE;
}


